package com.luke.xiaotiaowa.modules.ums.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.luke.xiaotiaowa.bo.AdminUserDetails;
import com.luke.xiaotiaowa.events.XtwEvents;
import com.luke.xiaotiaowa.modules.ums.cache.UmsAdminCacheService;
import com.luke.xiaotiaowa.modules.ums.cache.UmsPermCacheService;
import com.luke.xiaotiaowa.modules.ums.entity.UmsAdminEntity;
import com.luke.xiaotiaowa.modules.ums.entity.UmsAdminRoleRelationEntity;
import com.luke.xiaotiaowa.modules.ums.mapper.UmsAdminMapper;
import com.luke.xiaotiaowa.modules.ums.mapper.UmsAdminRoleRelationMapper;
import com.luke.xiaotiaowa.modules.ums.service.UmsAdminRoleRelationService;
import com.luke.xiaotiaowa.modules.ums.service.UmsAdminService;
import com.luke.xiaotiaowa.modules.ums.service.UmsPermService;
import com.luke.xiaotiaowa.utils.JwtTokenUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * <p>
 * 后台管理员表 服务实现类
 * </p>
 *
 * @author xiaotiaowa
 * @since 2023-03-22
 */
@Slf4j
@Service
public class UmsAdminServiceImpl implements UmsAdminService {

    @Resource
    private UmsAdminMapper umsAdminMapper;
    @Resource
    private UmsAdminRoleRelationMapper adminRoleRelationMapper;
    @Resource
    private UmsPermService umsPermService;
    @Resource
    private UmsAdminRoleRelationService adminRoleRelationService;
    @Resource
    private JwtTokenUtil jwtTokenUtil;
    @Resource
    private PasswordEncoder passwordEncoder;
    @Resource
    private UmsAdminCacheService adminCacheService;
    @Resource
    private UmsPermCacheService permCacheService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String login(String username, String password) {
        String token = null;
        //密码需要客户端加密后传递
        try {
            var userDetails = loadUserByUsername(username);
            if (!passwordEncoder.matches(password, userDetails.getPassword())) {
                throw XtwEvents.BAD_REQUEST.get().message("密码不正确");
            }
            if (!userDetails.isEnabled()) {
                throw XtwEvents.BAD_REQUEST.get().message("帐号已被禁用");
            }
            var authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(authentication);
            token = jwtTokenUtil.generateToken(userDetails);

            // 更新登录时间
            var admin = selectUmsAdminByUsername(username);
            admin.setLoginTime(LocalDateTime.now());
            umsAdminMapper.updateById(admin);
        } catch (AuthenticationException e) {
            log.warn("登录异常:{}", e.getMessage());
        }
        return token;
    }

    @Override
    public String refreshToken(String oldToken) {
        return jwtTokenUtil.refreshHeadToken(oldToken);
    }

    @Override
    public List<UmsAdminEntity> selectUmsAdmin(IPage<UmsAdminEntity> page, String keyword) {
        return umsAdminMapper.selectListByParams(page, keyword);
    }

    @Override
    public UmsAdminEntity selectUmsAdmin(long id) {
        var admin = umsAdminMapper.selectById(id);
        XtwEvents.BAD_REQUEST.get().message("用户不存在")
                .ifTrueThrow(ObjectUtil.isNull(admin));
        return admin;
    }

    @Override
    public UserDetails loadUserByUsername(String username) {
        //获取用户信息
        var admin = selectUmsAdminByUsername(username);
        return Optional.ofNullable(admin)
                .map(v -> {
                    var perms = umsPermService.getPerms(v.getId());
                    return new AdminUserDetails(admin, perms);
                })
                .orElseThrow(() -> XtwEvents.BAD_REQUEST.get().message("用户名或密码错误"));
    }

    @Override
    public UmsAdminEntity selectUmsAdminByUsername(String username) {
        var adminCache = adminCacheService.getAdminByUsername(username);
        return Optional.ofNullable(adminCache)
                .orElseGet(() -> {
                    var admin = umsAdminMapper.selectByUsername(username);
                    if (ObjectUtil.isNotNull(admin)) {
                        adminCacheService.setAdmin(admin);
                    }
                    return admin;
                });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UmsAdminEntity addUmsAdmin(UmsAdminEntity umsAdmin) {
        var admin = selectUmsAdminByUsername(umsAdmin.getUsername());
        XtwEvents.BAD_REQUEST.get().message("用户名已存在").ifTrueThrow(ObjectUtil.isNotNull(admin));
        umsAdmin.setPassword(passwordEncoder.encode(umsAdmin.getPassword()));
        umsAdminMapper.insert(umsAdmin);
        adminCacheService.setAdmin(umsAdmin);
        return umsAdmin;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public UmsAdminEntity modUmsAdmin(long id, UmsAdminEntity umsAdmin) {
        // 删除缓存
        adminCacheService.delAdmin(umsAdmin.getUsername());
        delayDoubleDelCache(umsAdmin.getUsername(), () -> {
            umsAdmin.setId(id);
            var oldAdmin = selectUmsAdmin(id);
            var adminByName = selectUmsAdminByUsername(umsAdmin.getUsername());
            XtwEvents.BAD_REQUEST.get().message("用户名已存在")
                    .ifTrueThrow(ObjectUtil.isNotNull(adminByName) && !adminByName.getId().equals(umsAdmin.getId()));
            umsAdmin.setPassword(passwordEncoder.encode(umsAdmin.getPassword()));
            umsAdmin.setVersion(oldAdmin.getVersion());
            umsAdmin.setPassword(oldAdmin.getPassword());
            // 更新数据库
            umsAdminMapper.updateById(umsAdmin);
        });
        return umsAdmin;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delUmsAdmin(long id) {
        var admin = selectUmsAdmin(id);
        delayDoubleDelCache(admin.getUsername(), () -> {
            umsAdminMapper.deleteById(admin);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void activateUmsAdmin(long id) {
        var admin = selectUmsAdmin(id);
        delayDoubleDelCache(admin.getUsername(), () -> {
            admin.setStatus(true);
            umsAdminMapper.updateById(admin);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void suspendedUmsAdmin(long id) {
        var admin = selectUmsAdmin(id);
        delayDoubleDelCache(admin.getUsername(), () -> {
            admin.setStatus(false);
            umsAdminMapper.updateById(admin);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void assignRoles(long id, List<Long> roles) {
        var admin = selectUmsAdmin(id);
        permCacheService.delPerm(id);
        adminRoleRelationMapper.deleteByAdminId(id);
        var relations = roles.stream()
                .map(r -> {
                    var relation = new UmsAdminRoleRelationEntity();
                    relation.setRoleId(r);
                    relation.setAdminId(admin.getId());
                    return relation;
                }).collect(Collectors.toList());
        Optional.of(relations)
                .filter(v -> v.size() > 0)
                .ifPresent(v -> adminRoleRelationService.saveBatch(relations));
        permCacheService.delPermAsync(id, 3000);
    }
    
    private void delayDoubleDelCache(String username, Runnable runnable) {
        adminCacheService.delAdmin(username);
        runnable.run();
        adminCacheService.delAdminAsync(username, 3000);
    }
}
